package crypto

import (
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"time"

	"golang.org/x/crypto/ssh"
)

func NewSsh() *sshHelper {
	return &sshHelper{
		protocol: "tcp",
		address:  ":22",
		username: "root",
		password: "root",
	}
}

type sshHelper struct {
	protocol string
	address  string
	username string
	password string
}

func (r *sshHelper) Test() {
	// ssh.Unmarshal()
	// ssh.Marshal()
	// ssh.BannerDisplayStderr()
	// ssh.FingerprintLegacyMD5()
	// ssh.FingerprintSHA256()
	// ssh.FixedHostKey()
	// ssh.GSSAPIWithMICAuthMethod()
	// ssh.RetryableAuthMethod()
	// ssh.NewClient()
	// ssh.NewClientConn()
	// ssh.NewCertSigner()
	// ssh.NewSignerFromKey()
	// ssh.NewSignerFromSigner()
	// ssh.NewPublicKey()
}
func (r *sshHelper) Server() {
	// privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
	// if err != nil {
	// 	panic(err)
	// }
	// privateBytes := x509.MarshalPKCS1PrivateKey(privateKey)

	// privateBytes, err := ioutil.ReadFile("authorized_keys")
	privateBytes, err := ioutil.ReadFile("private_key")
	if err != nil {
		panic(err)
	}

	private, err := ssh.ParsePrivateKey(privateBytes)
	// private, err := ssh.NewCertSigner(&privateBytes.(ssh.Certificate), private)
	if err != nil {
		panic(err)
	}

	config := &ssh.ServerConfig{
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			return nil, nil
		},
		PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
			if c.User() == r.username && string(pass) == r.password {
				return nil, nil
			}

			return nil, fmt.Errorf("password rejected for %s", c.User())
		},
	}
	config.AddHostKey(private)
	// key5, err := ssh.NewPublicKey(ssh.PublicKeys())
	// if err != nil {
	// 	panic(err)
	// }
	// ssh.PublicKeysCallback()

	listener, err := net.Listen(r.protocol, r.address)
	if err != nil {
		panic(err)
	}
	defer func(listener net.Listener) {
		err := listener.Close()
		if err != nil {
			panic(err)
		}
	}(listener)

	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Fatal("Failed to accept incoming connection:", err)
		}

		sshConn, chans, reqs, err := ssh.NewServerConn(conn, config)
		if err != nil {
			log.Println("Failed to handshake:", err)
			continue
		}

		log.Printf("SSH connection established from %s\n", sshConn.RemoteAddr())

		go ssh.DiscardRequests(reqs)
		go handleChannels(chans)
	}
}
func (r *sshHelper) Client() []byte {
	conn, err := ssh.Dial(r.protocol, r.address, &ssh.ClientConfig{
		User: r.username,
		Auth: []ssh.AuthMethod{
			ssh.Password(r.password),
		},
		Timeout:         5 * time.Second,
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	})
	if err != nil {
		panic(err)
	}
	defer func(conn *ssh.Client) {
		err := conn.Close()
		if err != nil {
			panic(err)
		}
	}(conn)

	session, err := conn.NewSession()
	if err != nil {
		panic(err)
	}
	defer func(session *ssh.Session) {
		err := session.Close()
		if err != nil {
			panic(err)
		}
	}(session)

	output, err := session.CombinedOutput("ls -l")
	if err != nil {
		panic(err)
	}

	return output
}

func handleChannels(chans <-chan ssh.NewChannel) {
	for newChannel := range chans {
		if newChannel.ChannelType() != "session" {
			newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
			continue
		}

		channel, requests, err := newChannel.Accept()
		if err != nil {
			log.Println("Failed to accept channel:", err)
			continue
		}

		go handleRequests(requests)
		go handleData(channel)
	}
}
func handleRequests(reqs <-chan *ssh.Request) {
	for req := range reqs {
		if req.Type == "exec" {
			// req.Payload
			req.Reply(true, nil)
		}
	}
}
func handleData(channel ssh.Channel) {
	defer channel.Close()
}
